package com.cts.tmall.app.filter;

import com.cts.tmall.com.exception.SystemException;
import com.cts.tmall.com.message.RspCode;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.StreamUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 替换 HttpServletRequest 支持重复读
 *
 * @author leibing.song
 * @since 2020-07-16
 */
@WebFilter(urlPatterns = "/*", filterName = "requestFilter")
public class HttpServletRequestFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request = request instanceof HttpServletRequest ? new BodyHttpServletRequest((HttpServletRequest) request)
                : request;
        chain.doFilter(request, response);
    }

    @Getter
    @Setter
    private static class BodyHttpServletRequest extends HttpServletRequestWrapper {
        // 存放 body 字节数组
        private byte[] body;

        public BodyHttpServletRequest(HttpServletRequest request) {
            super(request);
            readBody(request);
        }

        /**
         * 读取Request数据流到内存
         *
         * @param request
         */
        private void readBody(HttpServletRequest request) {
            try (InputStream inputStream = request.getInputStream()) {
                body = StreamUtils.copyToByteArray(inputStream);
            } catch (Exception e) {
                throw new SystemException(RspCode.FAILURE_SYSTEM, "读取HTTP请求数据流失败！", e);
            }
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }

                @Override
                public boolean isReady() {
                    return false;
                }

                @Override
                public void setReadListener(ReadListener listener) {
                    throw new UnsupportedOperationException();
                }

                @Override
                public int read() throws IOException {
                    return inputStream.read();
                }
            };
        }
    }
}
